package it.unibz.inf.cn;

import it.unibz.inf.cn.gui.Login;
import it.unibz.inf.cn.gui.MainWindow;

import java.awt.Desktop;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URLEncoder;
import java.util.Vector;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class Client {
	
	private Login log = null;
	private PropertyService propertyService = null;
	private String token = null;
	private String tokenSecret = null;
	private Autorization authorization= null;
	private JFrame loginWin = null;
	private MainWindow win = null;
	
	private Document getDocument(HttpURLConnection httpURLConnection) throws Exception {
		// Sign the request
		authorization.sign(httpURLConnection);
		// Create DOM document
		return Request.getDocument(httpURLConnection);
	}
	
	private Document getDocument(String url) throws Exception {
		// Create request 
		return getDocument(Request.getHttpURLConnection(url));
	}
	
	private Document getDocument(String url, String type) throws Exception {
		// Create request 
		return getDocument(Request.getHttpURLConnection(url, type));
	}
	
	private String getScreenName(Document doc) {
		String returnedScreenName = null;
		NodeList nodeList = doc.getElementsByTagName("user");
	    for (int s = 0; s < nodeList.getLength(); s++) {
	    	Node firstNode = nodeList.item(s); 
	    	if (firstNode.getNodeType() == Node.ELEMENT_NODE) {
	    		Element firstElement = (Element) firstNode;
	    		returnedScreenName = firstElement.getElementsByTagName("screen_name").item(0).getChildNodes().item(0).getNodeValue();
	    	}
	    }
	    return returnedScreenName;
	}
	
	private String[] getScreenNames(Document doc) {
		NodeList nodeList = doc.getElementsByTagName("user");
	    String[] screenNames = new String[nodeList.getLength()];
	    for (int s = 0; s < nodeList.getLength(); s++) {
	    	Node firstNode = nodeList.item(s); 
	    	if (firstNode.getNodeType() == Node.ELEMENT_NODE) {
	    		Element firstElement = (Element) firstNode;
	    		screenNames[s] = firstElement.getElementsByTagName("screen_name").item(0).getChildNodes().item(0).getNodeValue();
	    	}
	    }
	    return screenNames;
	}
	
	public Client() throws Exception {
		authorization = new Autorization();
	}
	
	public void startLogin(){
		loginWin = new JFrame();
		log = new Login(this);
		loginWin.setContentPane(log);
		loginWin.setSize(300, 180);
		loginWin.setLocationRelativeTo(null);
		loginWin.setVisible(true);
		loginWin.setDefaultCloseOperation(JDialog.EXIT_ON_CLOSE);
	}
	
	public void setUsername(String username) throws Exception{
		// Init properties
		propertyService = new PropertyService(username);
		String[] generatedToken = propertyService.getToken();
		if (generatedToken == null) {
			URI uri = authorization.getRegistrationUri();
			Desktop desk = Desktop.getDesktop();
			desk.browse(uri);
			log.showPIN();
		} else {
			log.setVisible(false);
			token = generatedToken[0];
			tokenSecret = generatedToken[1];
			startClient();
		}
	}
	
	public void setPinWithUsername(String pin) throws Exception{
		String[] generatedToken = authorization.retrieveAccessToken(pin);
		token = generatedToken[0];
		tokenSecret = generatedToken[1];
		log.setVisible(false);
		propertyService.storeToken(token, tokenSecret);
		startClient();
	}
	
	private void startClient(){
		try {
			loginWin.setVisible(false);
			authorization = new Autorization(token, tokenSecret);
			
			win = new MainWindow(this);
			JFrame mainwin = new JFrame();
			mainwin.setContentPane(win);
			mainwin.setLocationRelativeTo(null);
			mainwin.setSize(600, 400);
			mainwin.setVisible(true);
			mainwin.setDefaultCloseOperation(JDialog.EXIT_ON_CLOSE);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	 public static String xmlToString(Node node) {
	        try {
	            Source source = new DOMSource(node);
	            StringWriter stringWriter = new StringWriter();
	            Result result = new StreamResult(stringWriter);
	            TransformerFactory factory = TransformerFactory.newInstance();
	            Transformer transformer = factory.newTransformer();
	            transformer.transform(source, result);
	            return stringWriter.getBuffer().toString();
	        } catch (TransformerConfigurationException e) {
	            e.printStackTrace();
	        } catch (TransformerException e) {
	            e.printStackTrace();
	        }
	        return null;
	    }
	
	public String[] loadFollowers() {
		String[] screenNames = null;
		try {
			// Get the DOM document of the request 
			Document doc = getDocument("http://api.twitter.com/1/statuses/friends.xml");
			
		    // Parse the result
		    screenNames = getScreenNames(doc);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return screenNames;
	}
	
	public String[] getSearchResults(String query) {
		String[] screenNames = null;
		try {
			// Get the DOM document of the request
			Document doc = getDocument("http://api.twitter.com/1/users/search.xml?q=" + URLEncoder.encode(query, "UTF-8"));
			
		    // Parse the result
			screenNames = getScreenNames(doc);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return screenNames;
	}
	
	public Boolean unfollowUser(String screen_name) {
		String returnedScreenName = null;
		try {
			// Get the DOM document of the request
			Document doc = getDocument("http://api.twitter.com/1/friendships/destroy.xml"+
					"?screen_name=" + URLEncoder.encode(screen_name, "UTF-8"),
					Request.POST);
			
		    // Parse the result
		    returnedScreenName = getScreenName(doc);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return returnedScreenName != null && returnedScreenName.equals(screen_name);
	}

	public boolean followUser(String screen_name) {
		String returnedScreenName = null;
		try {
			// Get the DOM document of the request
			Document doc = getDocument("http://api.twitter.com/1/friendships/create.xml"+
					"?screen_name=" + URLEncoder.encode(screen_name, "UTF-8"),
					Request.POST);
			
		    // Parse the result
			returnedScreenName = getScreenName(doc);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return returnedScreenName != null && returnedScreenName.equals(screen_name);
	}
	
	public Vector<String> getLast100Tweets() throws Exception{
		Vector<String> results = new Vector<String>();
		String url = "http://api.twitter.com/1/statuses/home_timeline.xml";
		HttpURLConnection con = (HttpURLConnection) Request.getHttpURLConnection(url);
		authorization.sign((HttpURLConnection) con);
		con.connect();
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		Document doc = db.parse(con.getInputStream());
		doc.getDocumentElement().normalize();
//		System.out.println(xmlToString(doc));
		NodeList nodeList = doc.getElementsByTagName("status");
	    for (int s = 0; s < nodeList.getLength(); s++) {
	    	Node firstNode = nodeList.item(s); 
	    	if (firstNode.getNodeType() == Node.ELEMENT_NODE) {
	    		Element firstElement = (Element) firstNode;
	    		NodeList children = firstElement.getChildNodes();
	    		String text = "";
	    		String name = "";
	    		for(int i=0;i<children.getLength();i++){
	    			Node child = children.item(i);
	    			if (child.getNodeType() == Node.ELEMENT_NODE) {
	    	    		Element el = (Element) child;
	    	    		if(el.getNodeName().equals("text")){
	    	    			text = el.getTextContent();
	    	    		}else if(el.getNodeName().equals("user")){
		    				NodeList userlist = el.getChildNodes();
		    				for(int j=0; j<userlist.getLength();j++){
		    					Node userchild = userlist.item(j);
		    					if (userchild.getNodeType() == Node.ELEMENT_NODE) {
		    	    	    		Element u_el = (Element) userchild;
		    	    	    		if(u_el.getNodeName().equals("screen_name")){
		    	    	    			name = u_el.getTextContent();
		    	    	    		}
		    					}
		    				}
		    			}
	    			}
	    		}
	    		String tweet = "@"+name+":"+text+"\n";
	    		results.add(tweet);
	    	}
	    }
	    con.disconnect();
		return results;
	}
	
	public Autorization getAuthorization(){
		return authorization;
	}
	
	public void updateTweets() throws Exception{
		Vector<String> tweets = getLast100Tweets();
		win.writeTweets(tweets);
	}
	
	public void startUpdating(){
		Updater up = new Updater(this);
		try {
			up.startUpdateing();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
